//! 参数文件实现
//!
//! 该实现基于 linux 的 param 实现, 可以将一个数据结构与文件绑定,
//! 方便数据的快速交互访问.

use std::sync::{
    atomic::{
        AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
        AtomicU64, AtomicU8, AtomicUsize, Ordering,
    },
    Arc, Mutex,
};

use crate::{
    inode::{DfsInode, FileCreateOptions, InodeOperation},
    DfsError, DfsResult,
};

struct DfsParam {
    inner: Arc<dyn ParamOperation>,
}

impl DfsParam {
    /// 构建一个 `DfsParam`
    fn new(data: Arc<dyn ParamOperation>) -> Self {
        Self { inner: data }
    }
}

unsafe impl Send for DfsParam {}
unsafe impl Sync for DfsParam {}

/// `DfsParam` 结构 trait
///
/// 每个使用 param file 的结构都需要实现该 trait
#[allow(unused_variables)]
pub trait ParamOperation {
    /// 读取 param 数据并转换为 String
    fn read(&self) -> String;
    /// 将字符串数据写入
    ///
    /// # Errors
    /// 调用写文件, 文件实现返回结果
    fn wrtie(&self, buf: &str) -> DfsResult<()> {
        Err(DfsError::PermOperation)
    }
    /// 参数大小
    fn size(&self) -> usize {
        self.read().len()
    }
}

/// 参数文件创建选项
#[derive(Debug, Clone, Default)]
pub struct ParamCreateOptions {
    read: bool,
    write: bool,
    name: String,
}

impl ParamCreateOptions {
    /// 构建一个 param
    pub fn new() -> Self {
        Self::default()
    }

    /// param 文件可读
    pub fn read(&mut self, read: bool) -> &mut Self {
        self.read = read;
        self
    }

    /// param 文件可写
    pub fn write(&mut self, write: bool) -> &mut Self {
        self.write = write;
        self
    }

    /// param 文件可读可写
    pub fn read_write(&mut self, rdwr: bool) -> &mut Self {
        self.read(rdwr);
        self.write(rdwr);
        self
    }

    /// param 文件名字
    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = name.to_string();
        self
    }

    /// 创建 param file 文件
    ///
    /// # Errors
    /// 节点为文件, 创建属性, 格式不匹配等将返回错误
    pub fn create_param_file(
        &self,
        inode: Arc<DfsInode>,
        handler: Arc<dyn ParamOperation>,
    ) -> DfsResult<()> {
        let param_file = Arc::new(DfsParam::new(handler));
        FileCreateOptions::new()
            .read(self.read)
            .write(self.write)
            .name(&self.name)
            .create_file(inode, param_file)?;
        Ok(())
    }
}

impl InodeOperation for DfsParam {
    fn read(
        &self,
        _: &mut Box<dyn std::any::Any + Send>,
        buf: &mut String,
        _: &mut u64,
    ) -> DfsResult<usize> {
        *buf = self.inner.read();
        Ok(buf.len())
    }

    fn write(
        &self,
        _: &mut Box<dyn std::any::Any + Send>,
        buf: &str,
        _: &mut u64,
    ) -> DfsResult<usize> {
        self.inner.wrtie(buf)?;
        Ok(buf.len())
    }

    fn size(&self) -> usize {
        self.inner.size()
    }
}

macro_rules! create_param_file {
    ($ident:ident, $type1:ty, $type2:ty) => {
        impl ParamOperation for $type1 {
            fn read(&self) -> String {
                self.load(Ordering::Relaxed).to_string()
            }

            fn wrtie(&self, buf: &str) -> DfsResult<()> {
                let data = buf.parse::<$type2>().map_err(|_| DfsError::InvalidArgs)?;
                self.store(data, Ordering::Relaxed);
                Ok(())
            }
        }

        impl ParamCreateOptions {
            /// 创建参数文件
            ///
            /// # Errors
            /// 节点为文件, 创建属性, 格式不匹配等将返回错误
            pub fn $ident(&self, inode: Arc<DfsInode>, data: Arc<$type1>) -> DfsResult<()> {
                self.create_param_file(inode, data)
            }
        }
    };
}

create_param_file!(param_u8, AtomicU8, u8);
create_param_file!(param_i8, AtomicI8, i8);
create_param_file!(param_u16, AtomicU16, u16);
create_param_file!(param_i16, AtomicI16, i16);
create_param_file!(param_u32, AtomicU32, u32);
create_param_file!(param_i32, AtomicI32, i32);
create_param_file!(param_u64, AtomicU64, u64);
create_param_file!(param_i64, AtomicI64, i64);
create_param_file!(param_usize, AtomicUsize, usize);
create_param_file!(param_isize, AtomicIsize, isize);
create_param_file!(param_bool, AtomicBool, bool);

impl ParamOperation for Mutex<String> {
    fn read(&self) -> String {
        let lock = self.lock().unwrap();
        lock.clone()
    }

    fn wrtie(&self, buf: &str) -> DfsResult<()> {
        let mut lock = self.lock().unwrap();
        *lock = buf.to_string();
        Ok(())
    }

    fn size(&self) -> usize {
        let lock = self.lock().unwrap();
        lock.len()
    }
}

impl ParamCreateOptions {
    /// 创建参数文件
    ///
    /// # Errors
    /// 节点为文件, 创建属性, 格式不匹配等将返回错误
    pub fn param_string(&self, inode: Arc<DfsInode>, data: Arc<Mutex<String>>) -> DfsResult<()> {
        self.create_param_file(inode, data)
    }
}
